home *** CD-ROM | disk | FTP | other *** search
/ Collection of Internet / Collection of Internet.iso / infosrvr / dev / www_talk.930 / 000561_connolly@pixel.convex.com _Thu Jan 14 01:56:19 1993.msg < prev    next >
Internet Message Format  |  1994-01-24  |  8KB

  1. Return-Path: <connolly@pixel.convex.com>
  2. Received: from dxmint.cern.ch by  nxoc01.cern.ch  (NeXT-1.0 (From Sendmail 5.52)/NeXT-2.0)
  3.     id AA08306; Thu, 14 Jan 93 01:56:19 MET
  4. Received: by dxmint.cern.ch (5.65/DEC-Ultrix/4.3)
  5.     id AA18151; Thu, 14 Jan 1993 02:11:29 +0100
  6. Received: from pixel.convex.com by convex.convex.com (5.64/1.35)
  7.     id AA17773; Wed, 13 Jan 93 19:11:17 -0600
  8. Received: from localhost by pixel.convex.com (5.64/1.28)
  9.     id AA21994; Wed, 13 Jan 93 19:11:15 -0600
  10. Message-Id: <9301140111.AA21994@pixel.convex.com>
  11. To: www-talk@nxoc01.cern.ch
  12. Subject: suggested libWWW architecture
  13. Date: Wed, 13 Jan 93 19:11:15 CST
  14. From: Dan Connolly <connolly@pixel.convex.com>
  15.  
  16.  
  17. I sent this to tim a while ago, but I don't think
  18. he's had time to look at it.
  19.  
  20. Meanwhile, libWWW is becomming reentrant, but I still
  21. think the architecture is kinda clumsy: you have to
  22. have a big data structure describing the DTD, and
  23. a routine for each element, etc.
  24.  
  25. This doesn't mesh well with the MidasWWW architecture, which
  26. can read the DTD from the X resource database at
  27. runtime.
  28.  
  29. I have an idea for an architecture that the linemode and
  30. MidasWWW could share (along with other new implementations).
  31.  
  32. It's not radically different from the current libWWW, but
  33. there's a lot of grunt-work between the current libWWW
  34. and what I've got here. But I think the end result would
  35. be much more usable.
  36.  
  37. We start with the HText class. In stead of the various
  38. style and append methods, we have four methods in a
  39. virtual function table:
  40.  
  41. typedef struct{
  42.   int (*start_tag) PARAMS((SGML_Object this, CONST char* gi,
  43.                 CONST char** attributes, int nattrs));
  44.   VOID (*end_tag) PARAMS((SGML_Object this, CONST char* gi));
  45.  
  46.   VOID (*entity) PARAMS((SGML_Object this, CONST char* name));
  47.  
  48.   VOID (*data) PARAMS((SGML_Object this, CONST char* data, int char_qty));
  49. }SGML_DocClass;
  50.  
  51. The linemode would declare something like:
  52.  
  53. SGML_DocClass griddoc = {HText_start_tag, HText_end_tag,
  54.             HText_entity, HText_data};
  55.  
  56. The HText implementation is responsible for keeping track of
  57. the stack of open elements, if it needs to.
  58.  
  59. On top of these we build some format parsing routines:
  60.  
  61. SGML_parse(void* dest, void* closure, void* stream, int (getc)(void*));
  62. /* psuedocode:
  63.    int read, content;
  64.    char buffer[1000];
  65.    SGML_DocClass *docclass = (SGML_DocClass*)closure;
  66.  
  67.    while( (read = SGML_read(buffer, content, stream, getc)) != EOF){
  68.      switch(read){
  69.        case SGML_start_tag:
  70.          ... parse name, attributes ...
  71.          content = (docclass->startTag)(dest, name, attrs);
  72.          if(content = empty){
  73.            (docclass->endTag)(name);
  74.            content = MIXED; /*@@ could be ELEMENT */
  75.          }
  76.          break;
  77.  
  78.        case SGML_end_tag:
  79.          ... parse name ...
  80.          (docclass->endTag)(name);
  81.          content = MIXED; /*@@ could be ELEMENT */
  82.          break;
  83.  
  84.        case SGML_entity:
  85.          (docclass->entity)(data, name);
  86.          break;
  87.  
  88.        default:
  89.          (docclass->data)(dest, buffer);
  90.     }
  91. */
  92.  
  93. PlainText_parse(HText* dest, void* docclass, void* stream, int (getc)(void*));
  94. /* psuedocode:
  95.    (docclass->startTag)(dest, "HTML");
  96.    (docclass->startTag)(dest, "BODY");
  97.    (docclass->startTag)(dest, "PRE");
  98.    keep a local buffer of about 1000 chars.
  99.    Call (getc)(stream) until EOF.
  100.    Call HText_data(dest, buffer) whenever buffer is full.
  101.    (docclass->endTag)(dest, "PRE");
  102.    (docclass->endTag)(dest, "BODY");
  103.    (docclass->endTag)(dest, "HTML");
  104. */
  105.  
  106. GopherListing_parse(HText* dest, void* dummy, void* stream, int (getc)(void*));
  107. /* psuedocode:
  108.    (docclass->startTag)(dest, "HTML");
  109.    (docclass->startTag)(dest, "BODY");
  110.    (docclass->startTag)(dest, "MENU");
  111.    while(Gopher_parse_line(stream, getc, type, name, host, port, path)){
  112.       char addr[BIG];
  113.       sprintf(addr, "gopher://%s:%d/%c%s", host, port, type, path);
  114.       (docclass->startTag)(dest, "A",
  115.                        "HREF", addr,
  116.                        0);
  117.       (docclass->data)(dest, name);
  118.       (docclass->endTag)(dest, "A");
  119.    }
  120.    (docclass->endTag)(dest, "MENU");
  121.    (docclass->endTag)(dest, "BODY");
  122.    (docclass->endTag)(dest, "HTML"); 
  123. */
  124.  
  125.  
  126. We register each of these with the following routine:
  127.  
  128. int
  129. ContentType_register(CONST char* type, CONST char* subtype,
  130.         HTParseProc parse, void* closure);
  131.  
  132. For example:
  133.  
  134. main()
  135. {
  136.   ContentType_register("TEXT", "X-HTML", HTML_parse, griddoc);
  137.   ContentType_register("TEXT", "PLAIN", PlainText_parse, griddoc);
  138.   ContentType_register("APPLICATION", "X-GOPHER",
  139.              GopherListing_parse, griddoc);
  140. }
  141.  
  142.  
  143. The following routine can be used for any MIME entity. It will dispatch
  144. the appropriate parsing routine based on the content type header:
  145.  
  146. int
  147. ContentType_parse(const char* ct, HText* dest, void* stream, int (getc)(void*));
  148.  
  149.  
  150. Then we build some load routines, one per access scheme:
  151. (note that this design separates format from the access scheme, which
  152. allows us to, for example, load a gopher menu
  153. from a local file, or load HTML text from a Gopher server)
  154.  
  155. /* I don't have error handling worked out yet. We need to have a coherent
  156.    design for this. It's a mess in the current WWWlib. */
  157.  
  158. /* I think the WWW file: should be split into ftp: and local-file:.
  159.    It's cleaner to implement; there are precedents in the MidasWWW local:
  160.    scheme and the MIME ftp and local-file access-types. */
  161.  
  162. int
  163. LocalFile_load(HText* dest, CONST char* path, CONST char* search)
  164. {
  165.   FILE* stream;
  166.  
  167.   if(stream = fopen(path)){
  168.     const char* content_type = WWW_zen_content_type_from_extension(path);
  169.     ContentType_parse(content_type, dest, (void*)stream, (int ()(void*))getc);
  170.     fclose(stream);
  171.     return 1;
  172.   }else{
  173.     /* log an error */
  174.     return 0;
  175.   }
  176. }
  177.  
  178. int
  179. FTP_load(HText* dest, CONST char* path, CONST char* search);
  180.  
  181. int
  182. HTTP_load(HText* dest, CONST char* path, CONST char* search);
  183.  
  184. int
  185. Gopher_load(HText* dest, CONST char* path, CONST char* search);
  186. {
  187.   const char* content_type = Gopher_zen_content_type_from_gtype_char(*path);
  188.   char* host = HTParse(path, PARSE_HOST);
  189.   char* portnum = HTParse(path, PARSE_PORT);
  190.   int port = atoi(portnum);
  191.   static char* tab = "\007";
  192.   static char* crlf = "\015\012";
  193.  
  194.   void* stream = TCPOpen(host, port);
  195.  
  196.   if(stream){
  197.     TCPwrite(stream, path, strlen(path);
  198.     if(search){
  199.       TCPwrite(stream, tab, 1);
  200.       TCPwrite(stream, search, strlen(search);
  201.     }
  202.     TCPwrite(stream, crlf, 2);
  203.     ContentType_parse(content_type, dest, stream, TCPgetc);
  204.     TCPclose(stream);
  205.     return 1;
  206.    }else{
  207.     /* log an error */
  208.     return 0;
  209.    }
  210. }
  211.  
  212.  
  213. Then we register these just like formats:
  214.  
  215. HTAccess_register(const char* name, HTLoadProc load, void* closure);
  216.  
  217.  
  218. And the HTLoadDocument routine in HTAccess.c becomes this:
  219.  
  220. int
  221. HTAccess_load(HText* dest, HTParentAnchor* p, CONST char* address)
  222. {
  223.   char* scheme = HTParse(address, PARSE_SCHEME);
  224.   /* path is everything after the colon, except the anchor */
  225.   char* path = HTParse(address, PARSE_HOST|PARSE_PORT|PARSE_PATH);
  226.   char* anchor = HTParse(address, PARSE_ANCHOR);
  227.   char* search = HTParse(address, PARSE_SEARCH_TERMS);
  228.   HText dest = HText_new(p); /* check for doc already loaded in p @@ */
  229.   void* closure;
  230.   HTLoadProc load;
  231.  
  232.   if(load = /* load routine registered for scheme. find closure too */){
  233.     (load)(dest, path, search, closure);
  234.   }
  235.   HTSelect(dest, anchor);
  236. }
  237.  
  238.  
  239. What do you think?
  240.  
  241. Dan
  242.